import re
from .dao import UserDAO, FoodRecordDAO, FoodDAO, ExerciseDAO # Assuming User model is needed from .dao or .model
from .model import User # Explicitly import User model
from common.log import logger
from datetime import datetime


class HealthService:
    def __init__(self):
        self.user_dao = UserDAO()
        self.food_record_dao = FoodRecordDAO()
        self.food_dao = FoodDAO()
        self.exercise_dao = ExerciseDAO()

    def save_food_record(self, wx_id, nickname, content, img_path=None):
        """
        根据大模型返回的内容提取食物记录并保存到数据库中。
        """
        # 正则表达式提取总热量
        total_calories = self._extract_total_calories(content)
        logger.info(f"提取到的总热量为: {total_calories}")
        # if total_calories == 0.0:
        #     logger.error(f"[HealthService] 未提取到总热量，原文为：[{content}]")
        #     return None

        # 根据食物和热量保存食物记录
        food_record_id = self._save_food_record_in_db(wx_id, nickname, total_calories, content, img_path)

        return food_record_id

    def _extract_total_calories(self, content):
        """
        提取总热量值，使用正则表达式从内容中获取。
        整个表达式匹配的内容 是 完整匹配（group(0)）。
        第 1 组 (\d+(\.\d+)?)：
            这个括号内包含了 \d+(\.\d+)?，是第一个捕获组 group(1)。
            \d+：匹配整数部分（如 100）。
            (\.\d+)?：匹配可选的小数部分（如 .5）。
        第 2 组 (\.\d+)?：
            这个是 (\.\d+)? 的小数部分，是第二个捕获组 group(2)，用于匹配小数点和后面的数字（如 .5）。
        """
        match = re.search(r'总热量：?约?(\d+(\.\d+)?)千卡', content)
        if match:
            return float(match.group(1))
        return 0.0

    def _save_food_record_in_db(self, wx_id, nickname, total_calories, content, img_path=None):
        """
        将食物记录保存到food_record表，并将食物名称和热量保存到food表中。
        """
        # 获取用户ID
        user = self.user_dao.get_user_by_wx_id(wx_id)
        if not user:
            # Pass content=None to avoid parsing attempts if user is created here just for food record
            res = self.save_user_info(wx_id=wx_id, nickname=nickname, content=None)
            if res:
                user = self.user_dao.get_user_by_wx_id(wx_id)
            else:
                logger.error(f"[HealthService] 创建用户{wx_id}失败")
                return None
        
        if not user: # Double check user creation
             logger.error(f"[HealthService] 获取或创建用户{wx_id}失败，无法保存食物记录")
             return None


        # 提取食物信息
        food_items = self._extract_food_items(content)
        if not food_items:
            logger.error(f"[HealthService] 未提取到食物记录，原文为[{content}]")
            #return None # Decide if a record should be saved if no food items but nutrients are present

        # 提取营养成分信息
        nutrient_ratios = self._extract_nutrient_ratios(content)
        carbohydrate = nutrient_ratios.get('carbohydrate', 0.0) # Default to 0.0 if not found
        protein = nutrient_ratios.get('protein', 0.0)
        lipid = nutrient_ratios.get('lipid', 0.0)

        # Validate nutrient ratios only if they were found
        if nutrient_ratios: # Only validate if ratios were actually extracted
            rate_sum = carbohydrate + protein + lipid
            # Check individual rates and sum (approximately 100, allow for float precision issues if strict sum needed)
            if not (self.check_rate(carbohydrate) and self.check_rate(protein) and self.check_rate(lipid)) or not (99.0 < rate_sum < 101.0):
                logger.warning(f"[HealthService] 提取到的营养成分比例不合法或总和不为100%，原文为[{content}]. Ratios: C={carbohydrate}, P={protein}, L={lipid}")
                # Decide if this is a hard error or if we save with potentially imperfect data
                # For now, let's log a warning and proceed with what was extracted or defaults.
                # If strictness is required, could return None here or set ratios to 0.0.

        # 保存食物记录到 food_record 表
        food_record_id = self.food_record_dao.insert_record(user_id=user.user_id, img_path=img_path, total_calories=total_calories, carbohydrate=carbohydrate, protein=protein, lipid=lipid)
        logger.info(f"保存食物记录到 food_record 表，food_record_id: {food_record_id}")

        # 提取食物信息并保存到 food 表
        calculated_calories_from_items = 0.0
        if food_items: # Only iterate if food_items were extracted
            for food_item in food_items:
                self.food_dao.insert_food(food_item['food_name'], food_item['calories'], food_record_id)
                calculated_calories_from_items += food_item['calories']
            logger.info(f"保存{len(food_items)}个食物记录到 food 表")

        # If total_calories was initially 0.0 (e.g. not found in main summary but items were listed),
        # update it with the sum from individual items.
        if total_calories == 0.0 and food_items and calculated_calories_from_items > 0.0:
            self.food_record_dao.update_total_calories(food_record_id, calculated_calories_from_items)
            logger.info(f"Food record {food_record_id} total_calories updated to {calculated_calories_from_items} from items.")


        return food_record_id

    def _extract_nutrient_ratios(self, content):
        """
        提取营养成分占比（碳水化合物、蛋白质、脂类）
        使用正则表达式匹配格式：
            "碳水化合物占比约XX%，蛋白质占比约XX%，脂类占比约XX%"
        支持整数和小数形式
        """
        nutrient_ratios = {}

        # 优化后的正则表达式模式，支持小数
        pattern = r'碳水化合物占比约(\d+(\.\d+)?)%，蛋白质占比约(\d+(\.\d+)?)%，脂类占比约(\d+(\.\d+)?)%'
        match = re.search(pattern, content)

        if match:
            try:
                nutrient_ratios['carbohydrate'] = float(match.group(1))
                nutrient_ratios['protein'] = float(match.group(3))
                nutrient_ratios['lipid'] = float(match.group(5))
            except (ValueError, TypeError):
                logger.error(f"Error parsing nutrient ratios from content: {content}")
                return {}


        return nutrient_ratios

    def _extract_food_items(self, content):
        """
        提取食物名称和对应的热量
        ([^：:\n]+)      捕获组1：匹配食物名称，直到遇到冒号
        [：:]            匹配中文冒号 `：` 或 英文冒号 `:`
        (?:约)?	        非捕获组（?: 表示不存入匹配组），匹配可选的 "约"
        (\d+(\.\d+)?)	捕获组 2：匹配 卡路里数值（整数或小数）
        (\.\d+)?	    捕获组 3：匹配 小数部分（可选）
        千卡	            匹配 "千卡" 这个单位（不会被捕获
        """
        food_items = []
        food_pattern = r'([^.：:\n]+)\s*[：:]\s*(?:约)?\s*(\d+(\.\d+)?)\s*千卡'
        matches = re.finditer(food_pattern, content)
        for match in matches:
            food_name = match.group(1).strip()
            try:
                calories = float(match.group(2))
                if food_name == "总热量": # Skip if "总热量" is parsed as a food item
                    continue
                food_items.append({'food_name': food_name, 'calories': calories})
            except (ValueError, TypeError):
                logger.error(f"Error parsing calories for food item: {food_name} from content: {content}")
        return food_items

    def get_user_today_food_report(self, wx_id, nickname):
        """
        查询用户当天的所有饮食记录并返回一个总结字符串。
        """
        user = self.user_dao.get_user_by_wx_id(wx_id)
        if not user:
            # Pass content=None as no specific user info string is provided here
            res = self.save_user_info(wx_id=wx_id, nickname=nickname, content=None)
            if res:
                user = self.user_dao.get_user_by_wx_id(wx_id)
            else: # User creation failed
                logger.error(f"[HealthService] 创建用户{wx_id}失败 for report")
                return f"无法获取用户信息，请稍后再试或设置个人信息。"
        
        if not user: # Double check after attempt
             logger.error(f"[HealthService] 用户{wx_id}不存在且无法创建 for report")
             return f"无法获取用户信息，请稍后再试或设置个人信息。"
    
        # 获取当天的饮食记录
        food_records = self.food_record_dao.list_records_by_user(user.user_id,
                                                                 date_str=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        report = "📅 **今日健康报告** 🍽️🏋️\n\n" #
        total_calories_intake = 0
        if not food_records:
            # 处理空记录的情况
            report += "今日暂无饮食记录\n"
        else:
            report += "今日饮食记录：\n"
            count = 1
            for record in food_records:
                report += f"第 {count} 餐 (记录于 {datetime.strptime(record.record_time, '%Y-%m-%d %H:%M:%S').strftime('%H:%M')}):\n" # (Added time)
                count += 1
                foods = self.food_dao.list_foods_by_record(record.food_record_id)
                record_total = 0  # 总摄入热量
                if foods: #
                    for food in foods: #
                        record_total += food.calories #
                        report += f"- {food.food_name}: {food.calories:.1f}千卡\n" #
                elif record.total_calories > 0: # Fallback if no items but record has total
                    record_total = record.total_calories #
                    report += f"- (本餐总计): {record.total_calories:.1f}千卡\n" #
                else:
                    report += "- (未记录具体食物或热量)\n"
                
                total_calories_intake += record_total #
                # report += f"本餐总热量：{record_total:.1f}千卡\n" # Redundant if items listed, good if only total
                if record.img_path: 
                    report += f"图片参考：{record.img_path}\n" #
                report += "\n" 

        # 获取用户今日运动消耗的热量
        exercise_records = self.exercise_dao.list_exercises_by_user(user.user_id, date_str=datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S"))
        total_burned_calories = 0  # 今日运动总消耗的热量
        report += "--- 运动记录 ---\n" #
        if not exercise_records: #
            report += "今日暂无运动记录。\n" #
        else:
            for ex_record in exercise_records: 
                report += f"- {ex_record.exercise_name} (消耗 {ex_record.burned_calories:.1f}千卡，记录于 {datetime.strptime(ex_record.exercise_time, '%Y-%m-%d %H:%M:%S').strftime('%H:%M')})\n" # (Added time)
                total_burned_calories += ex_record.burned_calories #
        report += "\n" #
        # 获取用户的基础代谢率(BMR)和每日建议摄入热量 (TDEE)
        # User object should have bmr and tdee if info was complete
        bmr = user.bmr if user.bmr is not None else self._calculate_bmr(user) # Recalculate if not stored or prefer fresh
        tdee = user.tdee if user.tdee is not None else self._calculate_tdee(bmr, user.activity_level)
        
        # Fallback if bmr/tdee still not calculable (e.g. insufficient user info)
        if bmr is None or bmr == 0.0: # Check for None or 0.0
             bmr = 0.0 # Ensure it's float for formatting
        if tdee is None or tdee == 0.0:
             tdee = 0.0


        # --- 热量总结与缺口分析 ---
        report += "--- 今日热量总结 ---\n"
        report += f"总摄入热量：{total_calories_intake:.1f}千卡\n"
        report += f"运动消耗热量：{total_burned_calories:.1f}千卡\n"

        if tdee > 0: #
            report += f"预估每日能量消耗(TDEE)：{tdee:.1f}千卡\n"
            
            calorie_gap = total_calories_intake - total_burned_calories - tdee
            
            report += f"今日热量净值 (摄入 - 运动 - TDEE)：{calorie_gap:+.1f}千卡\n" # Using + sign for positive/negative

            if calorie_gap < 0:
                report += f"🎉 **祝贺您！** 今日成功制造了 **{-calorie_gap:.1f}千卡** 的热量缺口。继续保持健康的饮食和运动习惯！\n"
            elif calorie_gap == 0:
                 report += f"👍 **做得不错！** 您今日的热量摄入与预估消耗基本持平。\n"
            else: 
                report += f"⚠️ **温馨提示：** 今日热量超出了预算约 **{calorie_gap:.1f}千卡**。建议考虑增加适量的运动（如快走、慢跑等），或在后续饮食中适当调整，以帮助身体达到更好的能量平衡。\n"
        else:
            net_intake_after_exercise = total_calories_intake - total_burned_calories #
            report += f"今日净摄入热量 (摄入 - 运动)：{net_intake_after_exercise:.1f}千卡\n" #
            report += f"💡 提示：请发送 \"设置个人信息 ...\" 指令并提供完整的身高、体重、年龄、性别和当前活动水平，以便我为您估算每日能量消耗(TDEE)并提供更全面的热量平衡分析。\n" #
        


        return report #

    def _calculate_bmr(self, user: User): # Type hint for clarity
        """
        使用Harris-Benedict公式计算用户的基础代谢率（BMR）
        如果用户的个人信息不完善，因子为0，系统不会计算用户的每日推荐摄入热量
        """
        if not user.age or not user.gender or not user.height or not user.weight:
            return 0.0
        # Ensure gender is 1 (male) or 2 (female) for calculation
        if user.gender == 1:  # 男性
            bmr_val = 88.362 + (13.397 * user.weight) + (4.799 * user.height) - (5.677 * user.age)
        elif user.gender == 2:  # 女性
            bmr_val = 447.593 + (9.247 * user.weight) + (3.098 * user.height) - (4.330 * user.age)
        else:  # 未知性别 or gender == 0
            return 0.0
        return round(bmr_val, 2)

    def _calculate_tdee(self, bmr, activity_level):
            """
            计算总日能量消耗 (TDEE)
            """
            if bmr == 0.0 or not activity_level: # bmr can be 0.0 if info incomplete
                return 0.0
            factor = self._activity_level_factor(activity_level)
            return round(bmr * factor, 2)

    def _calculate_bmi(self, height_cm, weight_kg):
        """
        计算BMI指数
        BMI = 体重(kg) / (身高(m))^2
        """
        if not height_cm or not weight_kg or height_cm == 0: # also check height_cm is not None
            return None # Return None if data is insufficient
        height_m = height_cm / 100
        bmi_val = weight_kg / (height_m ** 2)
        return round(bmi_val, 2)

    def _activity_level_factor(self, activity_level):
        """
        根据活动水平返回活动因子
        """
        activity_factors = {
            "久坐不动": 1.2,
            "轻度活动": 1.375,
            "中度活动": 1.55,
            "高度活动": 1.725,
            "极度活动": 1.9
        }
        return activity_factors.get(activity_level, 1.375)  # 默认为轻度活动 if not found

    def save_exercise_record(self, wx_id, nickname, content):
        """
        根据用户输入的运动记录字符串，例如：
         "记录运动 跑步 消耗150千卡"
        来识别出 exercise_name 和 burned_calories，然后保存到数据库。
        """
        pattern = r"记录运动\s+(\S+)\s+消耗(\d+(\.\d+)?)\s*千卡" # Allow float for calories
        match = re.search(pattern, content)
        if not match:
            logger.error(f"[HealthService] save_exercise_record: 无法从'{content}'中识别到运动信息")
            return None

        exercise_name = match.group(1)
        try:
            burned_calories = float(match.group(2))
        except (ValueError, TypeError):
            logger.error(f"[HealthService] save_exercise_record: 无法从'{content}'中解析运动消耗热量")
            return None


        user = self.user_dao.get_user_by_wx_id(wx_id)
        if not user:
            created_user_success = self.save_user_info(wx_id, nickname, content=None) # Create basic user
            if created_user_success:
                user = self.user_dao.get_user_by_wx_id(wx_id)
            else:
                logger.error(f"[HealthService] 创建用户失败 wx_id={wx_id} for exercise record")
                return None
        
        if not user: # Double check
            logger.error(f"[HealthService] 获取或创建用户失败 wx_id={wx_id} for exercise record")
            return None

        exercise_id = self.exercise_dao.insert_exercise(exercise_name, user.user_id, burned_calories)
        logger.info(f"[HealthService] 已保存运动记录: {exercise_name}, 消耗{burned_calories}千卡, ID: {exercise_id}")
        return exercise_id

    # This is the first save_user_info function, now modified to include BMR/TDEE/BMI logic.
    def save_user_info(self, wx_id, nickname, content=None): #
        """
        根据用户输入的个人信息字符串，格式如：
          "设置个人信息 身高：170cm，体重：66kg，性别：男，年龄：21岁，活动水平：轻度活动"
        来识别、保存或更新用户信息，并计算和保存BMR, TDEE, BMI。
        如果content为空，则创建或更新基础用户信息。
        """
        _height = None #
        _weight = None #
        _gender = 0  # Default to 未知 (0) #
        _age = None #
        _activity_level = '轻度活动'  # Default activity level #

        if not content: #
            logger.warning(f"[HealthService] save_user_info: No content provided for {wx_id}. Will attempt to create/update basic info.") #
            # BMR/TDEE/BMI will not be calculated as no data is provided.
            # DAO defaults for age, activity_level will be used if creating new user without these fields.
        else:
            logger.info(f"[HealthService] save_user_info: Processing content for {wx_id}: {content}") #
            height_pattern = r'身高[:：]\s*(\d+(\.\d+)?)\s*cm' #
            weight_pattern = r'体重[:：]\s*(\d+(\.\d+)?)\s*kg' #
            gender_pattern = r'性别[:：]\s*(男|女)' #
            age_pattern = r'年龄[:：]\s*(\d+)\s*岁' #
            activity_pattern = r'活动水平[:：]\s*([\u4E00-\u9FA5]+)' #

            match_height = re.search(height_pattern, content) #
            if match_height: _height = float(match_height.group(1)) #

            match_weight = re.search(weight_pattern, content) #
            if match_weight: _weight = float(match_weight.group(1)) #

            match_gender = re.search(gender_pattern, content) #
            if match_gender: #
                gender_str = match_gender.group(1) #
                if gender_str == '男': _gender = 1 #
                elif gender_str == '女': _gender = 2 #
            
            match_age = re.search(age_pattern, content) #
            if match_age: _age = int(match_age.group(1)) #

            match_activity = re.search(activity_pattern, content) #
            if match_activity: #
                parsed_activity_level = match_activity.group(1).strip() #
                activity_levels = ["久坐不动", "轻度活动", "中度活动", "高度活动", "极度活动"] #
                if parsed_activity_level in activity_levels: #
                    _activity_level = parsed_activity_level #
                else:
                    logger.warning(f"[HealthService] Invalid activity level '{parsed_activity_level}' parsed for {wx_id}. Using default '{_activity_level}'.") #
            
            logger.info(f"[HealthService] Parsed from content for {wx_id}: height={_height}, weight={_weight}, gender={_gender}, age={_age}, activity_level={_activity_level}") #

        bmr, tdee, bmi = None, None, None #

        if _height is not None and _weight is not None and _age is not None: #
            temp_user_for_calc = User(user_id=None, wx_id=wx_id, nickname=nickname, height=_height, weight=_weight, gender=_gender, age=_age, activity_level=_activity_level) #
            bmr = self._calculate_bmr(temp_user_for_calc) #
            tdee = self._calculate_tdee(bmr, _activity_level) #
            bmi = self._calculate_bmi(_height, _weight) #
            logger.info(f"[HealthService] Calculated for {wx_id}: BMR={bmr}, TDEE={tdee}, BMI={bmi}") #
        else:
            logger.info(f"[HealthService] Insufficient data (height, weight, or age missing from content) to calculate BMR/TDEE/BMI for {wx_id}.") #

        existing_user = self.user_dao.get_user_by_wx_id(wx_id) #
        if not existing_user: #
            logger.info(f"[HealthService] Creating new user {wx_id} with all available info.") #
            new_user_id = self.user_dao.create_user( #
                wx_id=wx_id, nickname=nickname, gender=_gender,
                height=_height, weight=_weight, age=_age,
                activity_level=_activity_level,
                bmr=bmr, tdee=tdee, bmi=bmi
            )
            return new_user_id is not None #
        else:
            logger.info(f"[HealthService] Updating user {wx_id} with all available info.") #
            return self.user_dao.update_user_info( #
                wx_id=wx_id, nickname=nickname, gender=_gender,
                height=_height, weight=_weight, age=_age,
                activity_level=_activity_level,
                bmr=bmr, tdee=tdee, bmi=bmi
            )

    # The second save_user_info function and _parse_user_info_content are now effectively superseded
    # by the enhanced first save_user_info for 'content'-based updates.
    # Depending on whether the second save_user_info had other uses (e.g. direct parameter updates without content string),
    # it might be kept, modified, or removed. Based on the prompt, its role for BMI etc. via content is removed.
    # For now, I will comment out the original second save_user_info and its helper _parse_user_info_content
    # to avoid confusion and ensure the modified first one is used as intended.

    # def _parse_user_info_content(self, content): # This was part of the second save_user_info
    #     """Helper to parse content string into a dictionary of user info."""
    #     parsed_info = {}
    #     height_pattern = r'身高[:：]\s*(\d+(\.\d+)?)\s*cm'
    #     weight_pattern = r'体重[:：]\s*(\d+(\.\d+)?)\s*kg'
    #     gender_pattern = r'性别[:：]\s*(男|女)'
    #     age_pattern = r'年龄[:：]\s*(\d+)\s*岁'
    #     activity_pattern = r'活动水平[:：]\s*([\u4E00-\u9FA5]+)'

    #     match_height = re.search(height_pattern, content)
    #     if match_height: parsed_info['height'] = float(match_height.group(1))

    #     match_weight = re.search(weight_pattern, content)
    #     if match_weight: parsed_info['weight'] = float(match_weight.group(1))

    #     match_gender = re.search(gender_pattern, content)
    #     if match_gender: parsed_info['gender'] = match_gender.group(1) # Keep as string '男'/'女' for this helper

    #     match_age = re.search(age_pattern, content)
    #     if match_age: parsed_info['age'] = int(match_age.group(1))

    #     match_activity = re.search(activity_pattern, content)
    #     if match_activity:
    #         activity = match_activity.group(1).strip()
    #         activity_levels = ["久坐不动", "轻度活动", "中度活动", "高度活动", "极度活动"]
    #         if activity in activity_levels:
    #             parsed_info['activity_level'] = activity
    #         else:
    #             logger.warning(f"Parsed invalid activity_level '{activity}' from content.")
    #             # Optionally set a default or leave it out
    #     return parsed_info


    # def save_user_info(self, wx_id, nickname, content=None, height=None, weight=None, gender=None, age=None, activity_level=None):
    #         """
    #         Original second save_user_info. As per request, the first save_user_info
    #         has been modified to handle BMI etc. from content, making this one's role for that redundant.
    #         This function might still be useful if direct parameter updates (height, weight, etc.) are needed
    #         without parsing a 'content' string. If not, it could be removed.
    #         """
    #         parsed_info = {}
    #         if content and not (height or weight or gender is not None or age or activity_level): # Only parse content if direct params are not overriding
    #             parsed_info = self._parse_user_info_content(content)

    #         _height = height if height is not None else parsed_info.get('height')
    #         _weight = weight if weight is not None else parsed_info.get('weight')
            
    #         _gender_val = 0 # Default
    #         if gender is not None: # Direct parameter
    #             _gender_val = gender
    #         elif 'gender' in parsed_info: # Parsed from content
    #             if parsed_info['gender'] == "男": _gender_val = 1
    #             elif parsed_info['gender'] == "女": _gender_val = 2
                
    #         _age = age if age is not None else parsed_info.get('age')
    #         _activity_level = activity_level if activity_level is not None else parsed_info.get('activity_level', '轻度活动')


    #         bmr, tdee, bmi = None, None, None
    #         if _height and _weight and _age and _gender_val is not None: # _gender_val 0 is 'Unknown' but calculable
    #             temp_user_for_calc = User(height=_height, weight=_weight, gender=_gender_val, age=_age, activity_level=_activity_level)
    #             bmr = self._calculate_bmr(temp_user_for_calc)
    #             if bmr and bmr > 0:
    #                 tdee = self._calculate_tdee(bmr, _activity_level)
    #             bmi = self._calculate_bmi(_height, _weight)

    #         existing_user = self.user_dao.get_user_by_wx_id(wx_id)
    #         if existing_user:
    #             logger.info(f"[HealthService] Updating user {wx_id} (from second save_user_info impl)")
    #             return self.user_dao.update_user_info(
    #                 wx_id=wx_id, nickname=nickname, gender=_gender_val,
    #                 height=_height, weight=_weight, age=_age,
    #                 activity_level=_activity_level,
    #                 bmr=bmr, tdee=tdee, bmi=bmi
    #             )
    #         else:
    #             logger.info(f"[HealthService] Creating new user {wx_id} (from second save_user_info impl)")
    #             new_user_id = self.user_dao.create_user(
    #                 wx_id=wx_id, nickname=nickname, gender=_gender_val,
    #                 height=_height, weight=_weight, age=_age,
    #                 activity_level=_activity_level,
    #                 bmr=bmr, tdee=tdee, bmi=bmi
    #             )
    #             return new_user_id is not None

    def _create_or_update_user_base(self, wx_id, nickname):
        """
        If user doesn't exist, creates one with wx_id and nickname.
        If user exists, updates their nickname.
        This is typically for cases where no detailed profile content is provided.
        """
        user = self.user_dao.get_user_by_wx_id(wx_id)
        if not user:
            # Create user with only wx_id and nickname; other fields will use DB defaults or None
            # DAO's create_user has defaults for age, gender, activity_level. BMR/TDEE/BMI will be None.
            new_user_id = self.user_dao.create_user(wx_id=wx_id, nickname=nickname)
            logger.info(f"[HealthService] Created basic user: wx_id={wx_id}, nickname={nickname}, ID: {new_user_id}")
            return new_user_id is not None
        else:
            # Update only nickname if that's all that's changed or provided
            updated = self.user_dao.update_user_info(wx_id=wx_id, nickname=nickname)
            logger.info(f"[HealthService] Updated basic user nickname: wx_id={wx_id}, success: {updated}")
            return updated


    def get_user_info(self, wx_id):
        return self.user_dao.get_user_by_wx_id(wx_id)

    def get_exercise_info(self, exercise_id):
        return self.exercise_dao.get_exercise_by_id(exercise_id)

    def check_rate(self, num):
        if num is None or num < 0 or num > 100: # check for None
            return False
        return True

    def check_energy(self, food_record_id):
        if food_record_id is None: # Check if food_record_id is None
            logger.warning("[HealthService] check_energy called with None food_record_id.")
            return ""
            
        food_record = self.food_record_dao.get_record_by_id(food_record_id)
        if food_record is None:
            logger.warning(f"[HealthService] check_energy: No food_record found for ID {food_record_id}.")
            return ""
        
        user = self.user_dao.get_user_by_user_id(food_record.user_id)
        if user is None:
            logger.warning(f"[HealthService] check_energy: No user found for user_id {food_record.user_id} associated with food_record_id {food_record_id}.")
            return ""


        # Get all food records for the user for today to calculate total daily intake
        today_str = datetime.now().strftime("%Y-%m-%d") # Use only date for daily aggregation
        all_today_food_records = self.food_record_dao.list_records_by_user(food_record.user_id, date_str=today_str)
        
        total_daily_calories = 0.0
        total_daily_lipid_calories = 0.0

        if not all_today_food_records:
            logger.info(f"[HealthService] check_energy: No food records found for user {user.user_id} today ({today_str}).")
            # If the current record is the only one, use its values
            if food_record.record_time.startswith(today_str): # Check if the current record is from today
                total_daily_calories = food_record.total_calories
                if food_record.lipid is not None and food_record.total_calories is not None:
                    total_daily_lipid_calories = food_record.total_calories * (food_record.lipid / 100.0) if food_record.lipid is not None else 0.0
        else:
            for record_item in all_today_food_records:
                current_record_total_calories = 0.0
                # Sum calories from individual food items if available, otherwise use record's total_calories
                foods_in_record = self.food_dao.list_foods_by_record(record_item.food_record_id)
                if foods_in_record:
                    for food in foods_in_record:
                        current_record_total_calories += food.calories
                else: # Fallback to the record's total_calories if no items detailed
                    current_record_total_calories = record_item.total_calories if record_item.total_calories is not None else 0.0

                total_daily_calories += current_record_total_calories
                if record_item.lipid is not None: # Ensure lipid percentage is available
                     total_daily_lipid_calories += current_record_total_calories * (record_item.lipid / 100.0)


        # Calculate user's TDEE (recommended daily calories)
        # Use stored BMR/TDEE if available and reliable, otherwise recalculate
        user_bmr = user.bmr if user.bmr is not None and user.bmr > 0 else self._calculate_bmr(user)
        user_tdee = user.tdee if user.tdee is not None and user.tdee > 0 else self._calculate_tdee(user_bmr, user.activity_level)
        
        # Fallback if TDEE cannot be calculated (e.g. insufficient user info)
        recommended_calories = user_tdee if user_tdee is not None and user_tdee > 0 else 0.0


        lipid_rate = (total_daily_lipid_calories / total_daily_calories * 100) if total_daily_calories > 0 else 0.0

        reply = ""
        literature = "\n《中国居民膳食指南（2022）》推荐合适的能量摄入比例为：碳水化合物占总能量的50%~65%，蛋白质占总能量的10%~15%，脂肪占总能量的20%~30%。"
        
        # Check against recommended calories (TDEE or a default if TDEE not set)
        calorie_threshold = recommended_calories if recommended_calories > 0 else 2200.0 # Use 2200 as default if TDEE not set
        warning_prefix = f"\n\n温馨提示：您今日膳食总热量{total_daily_calories:.1f}千卡"

        over_calories = False
        if total_daily_calories > calorie_threshold:
            over_calories = True
            if recommended_calories > 0:
                warning_prefix += f"，已超每日建议热量上限{calorie_threshold:.1f}千卡。"
            else: # Using default threshold because TDEE wasn't set
                 warning_prefix += f"，已超每日热量参考上限{calorie_threshold:.1f}千卡 (由于未设置完整的身体数据，假定您的每日推荐摄入热量为{calorie_threshold:.1f}千卡)。"
        
        lipid_warning = ""
        if total_daily_lipid_calories > 500.0: # General max, e.g., for a 2000 kcal diet, 25% fat is ~55g or 500 kcal
            lipid_warning = f"其中脂肪成分摄入{total_daily_lipid_calories:.1f}千卡，每日脂肪供能超过500千卡可能不利于健康。"
        elif lipid_rate > 30.0: # Percentage based, from guidelines
            lipid_warning = f"其中脂肪成分摄入{total_daily_lipid_calories:.1f}千卡，占比{lipid_rate:.1f}%，超过了指南中脂肪占比的推荐上限(30%)。"


        if over_calories:
            reply += warning_prefix
            if lipid_warning:
                reply += " " + lipid_warning + " 请适当调整自己的饮食。"
                if "占比" in lipid_warning: reply += literature # Add literature if % based warning
            else:
                reply += " 请适当调整自己的饮食。"
        elif lipid_warning: # Not over calories, but lipid warning
            reply += f"\n\n温馨提示：您今日膳食总热量{total_daily_calories:.1f}千卡。" + lipid_warning + " 请适当调整自己的饮食。"
            if "占比" in lipid_warning: reply += literature


        return reply
    
    def get_group_users_today_food_report(self, group_id=None):
        """
        获取有今日卡路里记录的用户的报告
        如果提供了group_id，则只获取该群组内有卡路里记录的用户的报告
        否则获取所有有卡路里记录的用户的报告
        """
        reports = []
        
        if group_id and group_id.endswith("@chatroom"):
            # 从iPadWx的shared_wx_contact_list中获取群组成员
            from channel.wechat.iPadWx import iPadWx
            
            if group_id in iPadWx.shared_wx_contact_list and 'chatRoomMembers' in iPadWx.shared_wx_contact_list[group_id]:
                group_members = iPadWx.shared_wx_contact_list[group_id]['chatRoomMembers']
                group_member_wxids = [member['userName'] for member in group_members if 'userName' in member]
                
                logger.info(f"[food_calorie] 群组 {group_id} 有 {len(group_member_wxids)} 个成员")
                
                # 获取所有用户
                users = self.user_dao.get_all_users()
                
                for user in users:
                    # 只处理群组内的用户
                    if user.wx_id in group_member_wxids:
                        # 获取用户今日的食物记录
                        food_records = self.food_record_dao.list_records_by_user(
                            user.user_id, date_str=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        )
                        
                        # 如果用户今日有食物记录，则生成报告
                        if food_records:
                            report = self.get_user_today_food_report(user.wx_id, user.nickname)
                            if report:
                                reports.append({
                                    "wx_id": user.wx_id,
                                    "nickname": user.nickname,
                                    "report": report
                                })
            else:
                logger.warning(f"[food_calorie] 无法获取群组 {group_id} 的成员列表")
                # 如果无法获取群组成员列表，则获取所有用户的报告（保持原有行为）
                users = self.user_dao.get_all_users()
                
                for user in users:
                    # 获取用户今日的食物记录
                    food_records = self.food_record_dao.list_records_by_user(
                        user.user_id, date_str=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    )
                    
                    # 如果用户今日有食物记录，则生成报告
                    if food_records:
                        report = self.get_user_today_food_report(user.wx_id, user.nickname)
                        if report:
                            reports.append({
                                "wx_id": user.wx_id,
                                "nickname": user.nickname,
                                "report": report
                            })
        else:
            # 如果没有提供group_id或者group_id不是群聊ID，则获取所有用户的报告（保持原有行为）
            users = self.user_dao.get_all_users()
            
            for user in users:
                # 获取用户今日的食物记录
                food_records = self.food_record_dao.list_records_by_user(
                    user.user_id, date_str=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                )
                
                # 如果用户今日有食物记录，则生成报告
                if food_records:
                    report = self.get_user_today_food_report(user.wx_id, user.nickname)
                    if report:
                        reports.append({
                            "wx_id": user.wx_id,
                            "nickname": user.nickname,
                            "report": report
                        })
        
        return reports